
package com.hlkj.pay.app.merchant.ext.impl.huifu;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import cn.hutool.json.JSONObject;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.framework.extension.executor.ExtensionExecutor;
import com.hlkj.framework.extension.scenario.BizScenario;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantOrderExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.huifu.dto.HuiFuConfig;
import com.hlkj.pay.app.merchant.ext.impl.huifu.handler.params.HuiFuPayOrderResponseParamsHandler;
import com.hlkj.pay.app.merchant.ext.impl.huifu.utils.HuiFuCallBackParamsParseUtil;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.dto.order.PayOrderChannelResultDto;
import com.hlkj.pay.dto.order.notify.CsOrderNotifyDto;
import com.hlkj.pay.dto.order.notify.RefundOrderNotifyDto;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.pay.PayWayDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.HttpRequestUtil;
import com.hlkj.pay.vo.order.req.PayOrderCommonParamReq;
import com.huifu.bspay.sdk.opps.core.utils.RsaUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Map;

/**
 * 汇付订单操作
 * 
 * @author HlpayTeam
 * @date 2024/10/10 13:51
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_HUI_FU_ZFT)
public class HuiFuZFTMerchantOrderExtPt implements ChannelMerchantOrderExtPt {

    private final ExtensionExecutor extensionExecutor;


    @Override
    public CommonResult<String> channelOrderCode(ChannelPayOrderContext channelPayOrderContext) {
        return CommonResult.success();
    }

    @Override
    public void submitChannelPayOrder(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        String huiFuPay = huiFuPay(payWayDO);
        if (!StringUtils.hasText(huiFuPay)) {
            channelPayOrderContext.setRemark("当前支付方式不支持");
            channelPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HuiFuScenePayOrderExtPt.class, BizScenario.valueOf(huiFuPay), ex -> ex.submitChannelPayOrder(channelPayOrderContext));
    }

    @Override
    public void queryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String huiFuPay = huiFuPay(payWayDO);
        if (!StringUtils.hasText(huiFuPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HuiFuScenePayOrderExtPt.class, BizScenario.valueOf(huiFuPay), ex -> ex.queryChannelPayOrder(channelPayOrderQueryContext));
    }

    @Override
    public void closeChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String huiFuPay = huiFuPay(payWayDO);
        if (!StringUtils.hasText(huiFuPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HuiFuScenePayOrderExtPt.class, BizScenario.valueOf(huiFuPay), ex -> ex.closeChannelPayOrder(channelPayOrderQueryContext));
    }

    @Override
    public void queryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String huiFuPay = huiFuPay(payWayDO);
        if (!StringUtils.hasText(huiFuPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HuiFuScenePayOrderExtPt.class, BizScenario.valueOf(huiFuPay), ex -> ex.queryChannelClosePayOrder(channelPayOrderQueryContext));

    }

    @Override
    public boolean refundApply(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        return true;
    }

    @Override
    public void refundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String huiFuPay = huiFuPay(payWayDO);
        if (!StringUtils.hasText(huiFuPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HuiFuScenePayOrderExtPt.class, BizScenario.valueOf(huiFuPay), ex -> ex.refundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public void queryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String huiFuPay = huiFuPay(payWayDO);
        if (!StringUtils.hasText(huiFuPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(HuiFuScenePayOrderExtPt.class, BizScenario.valueOf(huiFuPay), ex -> ex.queryRefundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        Map<String, String> params = HttpRequestUtil.requestFormParams(request);
        String reqBody = JsonUtils.toJsonString(params);
        log.info("汇付收到支付订单回调,参数数据为 payOrderDO:{}", reqBody);

        String paramsConfig = providerChannelConfigDO.getParamsConfig();
        PayOrderChannelResultDto payOrderChannelResultDto = new PayOrderChannelResultDto();
        PayOrderDO updartePayOrderDO = new PayOrderDO();
        payOrderChannelResultDto.setUpdatePayOrderDO(updartePayOrderDO);
        CommonResult<PayOrderChannelResultDto> payOrderDOCommonResult = CommonResult.success(payOrderChannelResultDto);

        try {
            // 配置转换
            HuiFuConfig huiFuConfig = JsonUtils.parseObject(paramsConfig, HuiFuConfig.class);
            HuiFuCallBackParamsParseUtil.verify(huiFuConfig, reqBody);

            JSONObject reqObj = JsonUtils.parseObj(reqBody);
            if (!"00000000".equals(reqObj.getStr("resp_code"))) {
                log.error("汇付支付回调返回失败，错误码：{}， 错误描述：{}", reqObj.getStr("resp_code"), reqObj.getStr("resp_desc"));
                payOrderDOCommonResult.setMsg(reqObj.getStr("resp_desc"));
                return payOrderDOCommonResult;
            }
            JSONObject respData = reqObj.getJSONObject("resp_data");
            if (!RsaUtils.verify(reqObj.getStr("resp_data"), huiFuConfig.getRsaPublicKey(), reqObj.getStr("sign"))) {
                log.error("汇付签名错误:{}", "验签失败");
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
                return payOrderDOCommonResult;
            }
            if (!"00000000".equals(respData.getStr("resp_code"))) {
                log.error("汇付支付回调业务返回失败，错误码：{}， 错误描述：{}", respData.getStr("resp_code"), respData.getStr("resp_desc"));
                payOrderDOCommonResult.setMsg(respData.getStr("resp_desc"));
                return payOrderDOCommonResult;
            }
            updartePayOrderDO.setPayOrderNo(respData.getStr("req_seq_id"));
            updartePayOrderDO.setInsOrderNo(respData.getStr("out_trans_id"));
            updartePayOrderDO.setChannelOrderNo(respData.getStr("party_order_id"));

            if (respData.containsKey("wx_response")) {
                JSONObject wxObj = respData.getJSONObject("wx_response");
                updartePayOrderDO.setUserId(wxObj.getStr("openid"));
            }

            if (respData.containsKey("alipay_response")) {
                JSONObject alipayObj = respData.getJSONObject("alipay_response");
                updartePayOrderDO.setUserId(alipayObj.getStr("buyer_id"));
            }

            if ("S".equals(respData.getStr("trans_stat"))) {
                updartePayOrderDO.setPayAmount(new BigDecimal(respData.getStr("pay_amt")));
                updartePayOrderDO.setSettlementAmount(new BigDecimal(respData.getStr("settlement_amt")));
                updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(respData.getStr("end_time")));
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
            } else if ("F".equals(respData.getStr("trans_stat"))) {
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.FAIL.getCode());
                updartePayOrderDO.setBankMsg(respData.getStr("bank_message"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return payOrderDOCommonResult;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> merAuthParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public PayOrderDO closeOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return HuiFuPayOrderResponseParamsHandler.closeOrderStatusParse(request);
    }

    @Override
    public CommonResult<CsOrderNotifyDto> csOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<RefundOrderNotifyDto> refundStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {

        return null;
    }


    @Override
    public String channelPayCode(PayWayDO payWayDO) {
        return huiFuPay(payWayDO);
    }

    @Override
    public Long queryChannelPayAppid(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        PayOrderDO payOrderDO = channelPayOrderContext.getPayOrderDO();
        PayOrderCommonParamReq.Extra extra = channelPayOrderContext.getExtra();
        if (extra == null && StringUtils.hasText(payOrderDO.getExtra())) {
            extra = JsonUtils.parseObject(payOrderDO.getExtra(), PayOrderCommonParamReq.Extra.class);
        }
        PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(payWayDO.getType());
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        ProviderChannelConfigDO providerChannelConfigDO = channelPayOrderContext.getProviderChannelConfigDO();
        if (extra != null && payType.getCode().equals(PayEnums.PAY_TYPE.WECHAT.getCode())) {
            switch (scenePayType) {
                case QR:
                case JSAPI:
                    return providerChannelConfigDO.getWechatDefaultPubId();
                case APP:
                case H5:
                case MINI_APP:
                    return providerChannelConfigDO.getWechatDefaultMiniId();
                default:
            }
        }

        return null;
    }
    @Override
    public String callBackResponse(CommonResult commonResult) {
        if (commonResult.isSuccess()) {
            return "RECV_ORD_ID_" + commonResult.getData();
        }
        else {
            return "0";
        }
    }

    @Override
    public boolean wechatLink(ChannelPayOrderContext channelPayOrderContext) {
        // 额外参数信息
        PayOrderCommonParamReq.Extra extra = channelPayOrderContext.getExtra();
        if(extra.getOriginalType() == CommonEnum.YES_NO_TYPE.NO.getCode()){
            PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.from(channelPayOrderContext.getPayWayDO().getSubType());
            if (scenePayType == null) {
                return false;
            }
            PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(channelPayOrderContext.getPayWayDO().getType());
            if(payType == null){
                return false;
            }
            switch (scenePayType) {
                case H5:
                case APP:
                case MINI_APP:
                    if(payType == PayEnums.PAY_TYPE.WECHAT){
                        return true;
                    }
            }
        }

        return false;
    }

    /**
     * 汇付支付方式路由
     * 
     * @param payWayDO
     * @return
     */
    private String huiFuPay(PayWayDO payWayDO) {
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByCode(payWayDO.getCode());
        if (scenePayType == null) {
            return null;
        }
        switch (scenePayType) {
            case APP:
            case H5:
            case WAP:
                return HuiFuExtConstant.HOSTING;
            case SCAN:
                return HuiFuExtConstant.QR_PAY;
            case MINI_APP:
            case JSAPI:
                return HuiFuExtConstant.JSAPI;
            default:
                return HuiFuExtConstant.SCAN_PAY;
        }
    }

    public static void main(String[] args) {
        String reqBody = "{\"resp_desc\":\"交易成功[000]\",\"resp_code\":\"00000000\",\"sign\":\"YYVkNi5tx+7xapjP0tmVDC+lMgT/pJRL0CuSy9ESQKupSnaMjL26nwCWzteFnzg8BMMhWMXLnJ87UrNC6FREhuEN843bpmVCHkdF6fL6iiB8GsI11TpdW3nYoKTfH1KEwDoMEhRsYreC67n4Taf5JR6ebkDaK3HAoHxEm7iH6Vyy5g+0wkwOwuPFoiRWbT0HYQ9dMFc8vt2xx+LV8XBXgP1pEx5cfOxjJkocm9y9Ips+oord7v5irb+zwp16jlmOLl/Z112qWwJBcJ60WMOh+E7Jz8NeZ5fIVEa9YAEYn1tDqtaRuEldq3ZfoF+l2/6V3CzIbNobFWOuaJ5m4ZvWDw==\",\"resp_data\":\"{\\\"acct_date\\\":\\\"20250416\\\",\\\"acct_id\\\":\\\"A42254241\\\",\\\"acct_split_bunch\\\":{\\\"acct_infos\\\":[{\\\"acct_date\\\":\\\"20250416\\\",\\\"acct_id\\\":\\\"A42254241\\\",\\\"div_amt\\\":\\\"5.62\\\",\\\"huifu_id\\\":\\\"6666000165033193\\\"}],\\\"fee_acct_date\\\":\\\"20250416\\\",\\\"fee_acct_id\\\":\\\"A42254241\\\",\\\"fee_amt\\\":\\\"0.02\\\",\\\"fee_huifu_id\\\":\\\"6666000165033193\\\"},\\\"acct_stat\\\":\\\"S\\\",\\\"atu_sub_mer_id\\\":\\\"757934151\\\",\\\"avoid_sms_flag\\\":\\\"\\\",\\\"bagent_id\\\":\\\"6666000162687756\\\",\\\"bank_code\\\":\\\"SUCCESS\\\",\\\"bank_desc\\\":\\\"交易成功\\\",\\\"bank_message\\\":\\\"交易成功\\\",\\\"bank_order_no\\\":\\\"4200002642202504165127709894\\\",\\\"bank_seq_id\\\":\\\"079360\\\",\\\"bank_type\\\":\\\"CCB\\\",\\\"base_acct_id\\\":\\\"A42254241\\\",\\\"batch_id\\\":\\\"250416\\\",\\\"channel_type\\\":\\\"U\\\",\\\"charge_flags\\\":\\\"762_0\\\",\\\"combinedpay_data\\\":[],\\\"combinedpay_fee_amt\\\":\\\"0.00\\\",\\\"debit_flag\\\":\\\"1\\\",\\\"debit_type\\\":\\\"D\\\",\\\"delay_acct_flag\\\":\\\"N\\\",\\\"div_flag\\\":\\\"0\\\",\\\"end_time\\\":\\\"20250416193608\\\",\\\"fee_amount\\\":\\\"0.02\\\",\\\"fee_amt\\\":\\\"0.02\\\",\\\"fee_flag\\\":2,\\\"fee_formula_infos\\\":[{\\\"fee_formula\\\":\\\"AMT*0.0038\\\",\\\"fee_type\\\":\\\"TRANS_FEE\\\"}],\\\"fee_rec_type\\\":\\\"1\\\",\\\"fee_type\\\":\\\"INNER\\\",\\\"fund_freeze_stat\\\":\\\"FREEZE\\\",\\\"gate_id\\\":\\\"VN\\\",\\\"hf_seq_id\\\":\\\"002900TOP3B250416193603P167ac13649400000\\\",\\\"huifu_id\\\":\\\"6666000165033193\\\",\\\"is_delay_acct\\\":\\\"0\\\",\\\"is_div\\\":\\\"0\\\",\\\"maze_resp_code\\\":\\\"\\\",\\\"mer_name\\\":\\\"长沙市天心区及贝百货店(个体工商户)\\\",\\\"mer_ord_id\\\":\\\"D20250416193603432079360\\\",\\\"mypaytsf_discount\\\":\\\"0.00\\\",\\\"need_big_object\\\":false,\\\"notify_type\\\":2,\\\"org_auth_no\\\":\\\"\\\",\\\"org_huifu_seq_id\\\":\\\"\\\",\\\"org_trans_date\\\":\\\"\\\",\\\"out_ord_id\\\":\\\"4200002642202504165127709894\\\",\\\"out_trans_id\\\":\\\"4200002642202504165127709894\\\",\\\"party_order_id\\\":\\\"03232504167056333324905\\\",\\\"pay_amt\\\":\\\"5.62\\\",\\\"pay_scene\\\":\\\"02\\\",\\\"posp_seq_id\\\":\\\"03232504167056333324905\\\",\\\"product_id\\\":\\\"PAYUN\\\",\\\"ref_no\\\":\\\"193603079360\\\",\\\"remark\\\":\\\"快递下单477\\\",\\\"req_date\\\":\\\"20250416\\\",\\\"req_seq_id\\\":\\\"D20250416193603432079360\\\",\\\"resp_code\\\":\\\"00000000\\\",\\\"resp_desc\\\":\\\"交易成功\\\",\\\"risk_check_data\\\":{\\\"ip_addr\\\":\\\"223.155.174.85\\\"},\\\"risk_check_info\\\":{\\\"client_ip\\\":\\\"223.155.174.85\\\"},\\\"settlement_amt\\\":\\\"5.62\\\",\\\"sub_resp_code\\\":\\\"00000000\\\",\\\"sub_resp_desc\\\":\\\"交易成功\\\",\\\"subsidy_stat\\\":\\\"I\\\",\\\"sys_id\\\":\\\"6666000162687756\\\",\\\"trade_type\\\":\\\"T_MINIAPP\\\",\\\"trans_amt\\\":\\\"5.62\\\",\\\"trans_date\\\":\\\"20250416\\\",\\\"trans_stat\\\":\\\"S\\\",\\\"trans_time\\\":\\\"193603\\\",\\\"trans_type\\\":\\\"T_MINIAPP\\\",\\\"ts_encash_detail\\\":[],\\\"wx_response\\\":{\\\"bank_type\\\":\\\"CCB\\\",\\\"coupon_fee\\\":\\\"0.00\\\",\\\"openid\\\":\\\"o8jhotx8VX3FyStiGxr9XhrOpuV8\\\",\\\"sub_appid\\\":\\\"wxe837f8c1601808bc\\\",\\\"sub_openid\\\":\\\"oTZRg7Bx4aVbpBcBQMD6JR4H7r8M\\\"}}\"}";

        String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoJgoX2wOXvxwmEeosMAoCPA69DYaHsjDGNCQBu24k98NB/9Y+UeOIgzmBKzBjUxEJLlowFtMm4MV6g02nk4faXRmAqJVoz68iB5wevPxR/5wkNpSsUUldN2DKjaRkVwHZvehbT1gpvll1/HY4keh/mZZIvXIDs2fG1smDT7oKo5OLuIVwKuv/4jr2lLystykCz2RrkJbpbpoKt7b738mlTo0VbdsxcWKUp62VnJPuctDHz5FC4xJ47JNSLyNBNbthrg0QjNnz8dk9/xInC8tdiwOpdakeFBNIDDHbRLanjmcEgL1ZrbCrmebbCwP5RrR7EFeqG2+l0VaXlw7SMUT7wIDAQAB";
        JSONObject reqObj = JsonUtils.parseObj(reqBody);

        if (!RsaUtils.verify(reqObj.getStr("resp_data"), publicKey, reqObj.getStr("sign"))) {
            System.out.println("汇付签名错误:验签失败");
        }
    }

}
